/*
 * Copyright (c) 2010-2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/gfx/ImageShape3DImpl.h"
#include "alkes/core/Memory.h"

namespace alkes {

ImageShape3DImpl* ImageShape3DImpl::create(IGraphics3D* graphics, uint32_t num)
{
    AL_PRE_COND(graphics && num, 0);

    return new ImageShape3DImpl(graphics, num);
}

void ImageShape3DImpl::destroy()
{
    delete this;
}

ImageShape3DImpl::ImageShape3DImpl(IGraphics3D* graphics, uint32_t num)
: ImageShape(num)
, graphics_(graphics)
, vbuffer_(0)
{
}

ImageShape3DImpl::~ImageShape3DImpl()
{
}

IVertexBuffer* ImageShape3DImpl::getVertexBuffer()
{
    if (vbuffer_)
        return vbuffer_;

    vbuffer_ = graphics_->createVertexBuffer(AL_VERTEX_XY|AL_VERTEX_COLOR|AL_VERTEX_UV1);

    if (!vbuffer_)
        return 0;

    uint32_t num = getArrayNum();

    uint32_t i;
    Color colors[4] = {
        Color::WHITE, Color::WHITE, Color::WHITE, Color::WHITE,
    };
    const uint32_t len = sizeof(colors)/sizeof(colors[0]);

    for (i = 0; i < num / len; ++i)
    {
        vbuffer_->setDiffuse(i * len, len, colors);
    }

    if (num % len)
    {
        vbuffer_->setDiffuse(i * len, num % len, colors);
    }

    return vbuffer_;
}

void ImageShape3DImpl::rect2VectorArray(const Rect& rect, Vector2D vec[4])
{
    float x1 = (float)rect.x;
    float y1 = (float)rect.y;
    float x2 = (float)rect.x + rect.width;
    float y2 = (float)rect.y + rect.height;

    vec[0].x = x1; vec[0].y = y1;
    vec[1].x = x2; vec[1].y = y1;
    vec[2].x = x1; vec[2].y = y2;
    vec[3].x = x2; vec[3].y = y2;
}

bool ImageShape3DImpl::renderTo(IRenderTarget3D* target, uint32_t start, uint32_t count)
{
    if (count == 0)
        return true;

    AL_PRE_COND(validate(start) && construct(target, getVertexBuffer()), false);

    uint32_t i;
    uint32_t num = start + count;

    if (getArrayNum() < num)
        num = getArrayNum();

    bool ret = target->setTexture(getImagePtr());
    if (ret)
    {
        for (i = start; i < num; ++i)
        {
            ret = target->drawPrimitive(vbuffer_, AL_PRIM_TRIANGLE_STRIP, i * 4, 4) && ret;
        }
        target->setTexture(0, 0);
    }

    return ret;
}

bool ImageShape3DImpl::construct(IRenderTarget3D* /*target*/, IVertexBuffer* vbuffer)
{
    if (!isDirty())
        return true;

    if (!vbuffer)
        return false;

    uint32_t i;
    for (i = 0; i < getArrayNum(); ++i)
    {
        setVertex(vbuffer, i, getImageRect(i));
    }

    setDirty(false);

    return true;
}

void ImageShape3DImpl::setVertex(IVertexBuffer* vbuffer, uint32_t index, const ImageRect& rect)
{
    // Set texture coord.
    {
        const Image& image(getImage());
        ImageBuffer* ibuffer(image.getImageBuffer());

        Rect src_rect;
        uint16_t x, y;
        image.getOffset(x, y);
        src_rect = rect.src_rect;
        src_rect.x += x;
        src_rect.y += y;

        uint16_t w, h;
        ibuffer->getSize(w, h);

        float u0 = (float)src_rect.x / w;
        float v0 = (float)src_rect.y / h;
        float u1 = (float)(src_rect.x + src_rect.width) / w;
        float v1 = (float)(src_rect.y + src_rect.height) / h;

        Vector2D tex[] = {
            {u0, v0}, {u1, v0}, {u0, v1}, {u1, v1},
        };

        vbuffer->setTexCoord(index * 4, 4, AL_VERTEX_UV1, tex);
    }

    // Set destination.
    {
        Vector2D vec[4];
        rect2VectorArray(rect.dst_rect, vec);
        vbuffer->setVertex2D(index * 4, 4, vec);
    }

    // Set color
    {
        Color colors[4] = {
            rect.color, rect.color, rect.color, rect.color
        };
        vbuffer->setDiffuse(index * 4, 4, colors);
    }
}

}
